APIFlask 初始化專案


Posted by monoserve174 on 2024-03-07

正式在資訊相關工作就職後,所有開發工作轉變為前後端分離的開發模式,別於筆者在先前學習的 Flask 全端開發模式。這期間也有嘗試性地將 Flask 做到前後端分離,結果還是乖乖的使用 Jinja 進行輸出畫面。當然依現在回顧起來,當時的做法其實可以更為優化,不過時光已過。近期,發現 Flask 開發者之一的李輝的推出了全新的 Resp Api 套件 (框架?) APIFlask,因此就來寫一篇以 APIFlask 作為後端 API 伺服器的文章吧。

APIFlask 介紹

APIFlask 是一個基於 Flask 的輕量級 Web 框架,專為建立 RESTful API 而設計。它結合了 Flask 與 marshmallow-code 並可自動化產生互動式 API 文件。

APIFlask 安裝指令

# Terminal
pip install apiflask

範例專案結構

- project dir
-- app.py

Hello APIFlask 最簡程式碼

# Python
# <project dir>/app.py
from apiflask import APIFlask

app = APIFlask(__name__)

@app.route('/')
def index():
    result = {
        message: 'Hello APIFlask'
    }
    return result

基本上只是將 Flask 引入 from flask import Flask 改為 APIFlask 關鍵字,這一改變 APIFlask 會幫我們自動生成互動式的 API 文件,讓我們繼續將程式運行起來吧

# Terminal
flask run

當終端機回應出
---
Running on http://127.0.0.1:5000
---
時,使用瀏覽器網址列輸入上述網址與 http://127.0.0.1:5000/docs 可發現,APIFlask 自動做出

  1. 將 Python Dict 型態轉為一般的 JSON 字典型態。
  2. 生成 Swagger UI 的互動式 API 文件。

接下來讓程式碼更 APIFlask 化吧

# Python
# <project dir>/app.py
from apiflask import APIFlask

app = APIFlask(__name__)

@app.get('/')
def index():
    result = {
        message: 'Hello APIFlask'
    }
    return result

這步驟將原先 Flask 裝飾器 route 改為 get ,能更明確的指出當前的 http 方法,輸出結果其實與未修改時相同。在過去 Flask 時,
@app.route('/') 更顯性的寫法如下
@app.route('/', methods=['GET'])
若要包含多種方法,則必須將方法放到 methods 中,並使用 flask 中的 request 判斷當前 http 方法,再用 Python 的判斷語句去控制不同的方法內容。在 APIFlask 提供了 get 、 post 、 put 與 delete 四個常用的 http 方法裝飾器,可以對相同的網址路徑,進行不同的邏輯控制。

實作小程式 - 1

在前後端分離的架構中,後端 API 伺服器扮演著資源提供者的角色,將各種功能和資料抽象化,並以 RESTful API 的形式提供給前端伺服器。這種模式使得前端可以更加專注於用戶界面和用戶體驗。下列我們舉出兩個例子來說明

  1. 取得所有飯店資訊
    假設我們有一個需求,需要取得所有飯店的資料。在這種情況下,前端伺服器會向後端 API 伺服器發出一個請求,例如發送一個 GET 請求到 /fruits 網址。後端接收到請求後,從資料庫中檢索所有飯店的資料,並以 JSON 格式回傳給前端伺服器。
  2. 取得登入資訊
    考慮使用者的登入功能。在過去,用戶的登入狀態可能是通過 cookies 或 sessions 在服務器端進行管理的。然而,在前後端分離的架構中,這些資訊也被抽象為一種資源,由前端伺服器負責管理。例如,當用戶嘗試登入時,前端伺服器會後端發出一個 POST 請求,包含使用者登入訊息。後端伺服器接收到請求後,驗證用戶名和密碼,並回傳一個包含用使用者資訊,由前端管理登入狀態管理。也由於登入狀態已改為前端伺服器處理了,後端也將淡化登出的內容管理。
    接著開始一起完成下列實作吧

第一部分: API 首頁與虛擬資料建構

# Python
# <project dir>/app.py
from apiflask import APIFlask

# 指定 app 實例化 APIFlask 類
app = APIFlask(__name__)

# 虛擬飯店列表
hotels = [
    {
        'id': 1,
        'name': 'Hotel A',
        'address': 'Taipei',
        'price': 3000
    },
    {
        'id': 2,
        'name': 'Hotel B',
        'address': 'Taichung',
        'price': 2500
    },
    {
        'id': 3,
        'name': 'Hotel C',
        'address': 'Kaohsiung',
        'price': 2000
    }
]

# API 首頁
@app.get('/')
@app.get('/apis')
def index():
    """
    API 首頁
    """
    result = {
        'message': 'Hello, APIFlask!'
    }
    return result

# 取得所有飯店資料
@app.get('/apis/hotels')
def get_hotels():
    """
    取得所有飯店資料
    """
    result = hotels
    return result

附帶參數的 Http 請求

一般狀況下,無法單純以網址及 Http 方法對於後端的資源取得,為達成更複雜的資料請求,附帶參數的 Http 請求相應而生,以下為筆者知道的附帶方法 URL Path Param URL Query Param 與 Request Body Param

  1. URL Path Param 是指在 URL 路徑中直接包含參數的方式。這種方式常用於 RESTful API 中,例如取得特定 ID 的資源。當我們需要取得 ID 為 123 的使用者資訊時,URL 就會類似於 "/users/123"
  2. URL Query Param 是指在 URL 的查詢字串中包含參數的方式。這種方式常用於搜尋或篩選資料。例如,我們可以使用查詢字串來搜尋名稱為 "John" 的使用者,URL 就會類似於 "/users/?name=John"
  3. Request Body Param 是指在 HTTP 請求的 body 中包含參數的方式。這種方式常用於 POST、PUT 或 PATCH 等方法,用於傳送資料到後端。例如,當我們需要創建一個新的使用者時,我們可以在請求的 body 中包含使用者的資訊。

在選擇使用哪種方法時,需要考慮以下幾點:

  • 安全性:URL Path Param 和 URL Query Param 的參數會在 URL 中顯示,可能會導致敏感資訊暴露。因此,對於敏感資訊,應該使用 Request Body Param。
  • 資料量:URL 的長度有限制,因此對於大量資料,使用 Request Body Param 是更好的選擇。
  • 請求方法:對於建立或更新資源,使用 POST、PUT 或 PATCH 方法並在 Request Body 中包含資料是最常見的做法。

實作小程式 - 2

此部分實作將銜接實作小程式 - 1 繼續進行

第二部分: 取得所有飯店資料增加篩選功能

功能簡述: 原取得所有飯店資料僅能直接將資源輸出,接下來增加關鍵字搜尋飯店,金額篩選飯店的篩選功能,此部分使用 URL Query Param 來實作,APIFlask 提供了 input 裝飾器搭配 Schema 來完成
增加三個關鍵字

  1. kw 飯店關鍵字
  2. min-price 最低金額
  3. max-price 最高金額
# Python
# <project dir>/app.py
from apiflask import APIFlask
from apiflask import Schema
from apiflask.fields import String, Integer

...

class HotelSearchInSchema(Schema):
    kw = String(description='關鍵字', required=False)
    min_price = Integer(description='最低價格', required=False)
    max_price = Integer(description='最高價格', required=False)

# 取得篩選飯店資料
@app.get('/apis/hotels')
@app.input(HotelSearchInSchema, location='query')
def get_hotels(**kwargs):
    """
    取得篩選飯店資料
    """
    query_params = kwargs.get('query_data', {})
    kw = query_params.get('kw', '')
    min_price = int(query_params.get('min_price', 0))
    max_price = int(query_params.get('max_price', 0))
    hotels_price = [hotel['price'] for hotel in hotels]
    if max_price == 0:
        max_price = max(hotels_price)

    result = []
    for hotel in hotels:
        if kw in hotel['name'] and min_price <= hotel['price'] <= max_price:
            result.append(hotel)

    return result

第三部分: 取得指定飯店資料

功能簡述: 透過飯店 id 取得指定飯店資訊,此部分使用 URL Path Param 來實作,APIFlask 提供動態的網址來完成

# Python
# <project dir>/app.py
...

@app.get('/apis/hotels/<int:hotel_id>')
def get_hotel(hotel_id):
    """
    取得單一飯店資料
    """
    result = [hotel for hotel in hotels if hotel['id'] == hotel_id][0]
    return result

第四部分: 新增一筆飯店資料

功能簡述: 輸入飯店的必填資料並將資料放入飯店資料內,其中必要欄位有

  1. name 飯店名稱
  2. address 飯店地址
  3. price 飯店價格

此部分使用 Request Body Param 來實作,APIFlask 提供了 input 裝飾器搭配 Schema 來完成

# Python
# <project dir>/app.py
...

class HotelCreateInSchema(Schema):
    name = String(description='飯店名稱', required=True)
    address = String(description='飯店地址', required=True)
    price = Integer(description='飯店價格', required=True)


@app.post('/apis/hotels')
@app.input(HotelCreateInSchema, location='json')
def create_hotel(**kwargs):
    """
    新增飯店資料
    """
    raw_data = kwargs.get('json_data', {})
    max_hotel_id = max([hotel['id'] for hotel in hotels])
    new_hotel = {
        'id': max_hotel_id + 1,
        'name': raw_data['name'],
        'address': raw_data['address'],
        'price': raw_data['price']
    }
    hotels.append(new_hotel)
    result = {
        'data': new_hotel,
        'message': 'Create Hotel Success!'
    }
    return result

到這部分,可以觀察出

  1. APIFlask 在實作 URL Query Param 與 Request Body Param 的方式基本上是相同的,都是以 input 裝飾器與 Schema 來完成,唯一的差別是在 location 部分,

    • URL Query Param: location='query'
    • Request Body Param: location='json'
  2. APIFlask 在實作 URL Path Param 與一般在操作動態的網址設定一致


#APIFlask #API Server #Init Project







Related Posts

C# Basic

C# Basic

Explore-Array and String

Explore-Array and String

Roadmap規劃

Roadmap規劃


Comments